#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include "sink.hpp"
#include "format.hpp"
#include "level.hpp"
#include "looper.hpp"
#include <vector>
#include <mutex>
#include <cstdarg>
#include <sstream>
#include <string>
#include <functional>
#include <iostream>
#include <unordered_map>

namespace MyLog
{
    /*
    日志器模块，是对前几个模块的一个整合（日志等级类、日志消息类、日志格式化类、日志落地方向类）
    分为同步日志器和异步日志器
    功能：创建一个日志器，日志器提供接口完成不同等级的日志输出
    */
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;

        Logger(Formatter::ptr formatter, 
                std::vector<LogSink::ptr> logSinks,
                LogLevel::Level level,
                std::string loggerName) 
                :_formatter(formatter)
                ,_logSinks(logSinks)
                ,_level(level)
                ,_loggerName(loggerName)
                {}
        
        void DEBUG(const char* file, size_t line, const char* fmt, ...)
        {
            va_list ap;
            va_start(ap, fmt);
            logHelper(LogLevel::Level::DEBUG, file, line, fmt, ap);
            va_end(ap);
        }

        void INFO(const char* file, size_t line, const char* fmt, ...)
        {
            va_list ap;
            va_start(ap, fmt);
            logHelper(LogLevel::Level::INFO, file, line, fmt, ap);
            va_end(ap);
        }

        void WARN(const char* file, size_t line, const char* fmt, ...)
        {
            va_list ap;
            va_start(ap, fmt);
            logHelper(LogLevel::Level::WARN, file, line, fmt, ap);
            va_end(ap);
        }

        void ERROR(const char* file, size_t line, const char* fmt, ...)
        {
            va_list ap;
            va_start(ap, fmt);
            logHelper(LogLevel::Level::ERROR, file, line, fmt, ap);
            va_end(ap);
        }

        void FATAL(const char* file, size_t line, const char* fmt, ...)
        {
            va_list ap;
            va_start(ap, fmt);
            logHelper(LogLevel::Level::FATAL, file, line, fmt, ap);
            va_end(ap);
        }

        //这里为了防止别人修改日志器名称，返回const &
        //或者直接传值返回
        const std::string& GetLoggerName()
        {
            return _loggerName;
        }

    protected:
        virtual void log(const char* data, size_t len) = 0;

        void logHelper(LogLevel::Level level, const char* file, size_t line, const char* fmt, va_list& ap)
        {
            if(static_cast<int>(_level) > static_cast<int>(level))
                return;

            // 对日记的有效载荷进行解析
            char* res = nullptr;
            if(vasprintf(&res, fmt, ap) == -1)
            {
                std::cout << "格式化日记有效载荷失败" << std::endl;
                assert(false);
            }
            va_end(ap); //将ap指针置空
            LogMsg msg(level, file, line, _loggerName, res);

            // 大问题
            
            // _mtx.lock();
            std::stringstream ss;
            _formatter->format(ss, msg);
            std::string s = ss.str();
            // _mtx.unlock();
            
            // 大问题

            // std::string s(100, 'a');
            // s += '\n';

            log(s.c_str(), s.size()); 

            free(res);
        }
    protected:
        Formatter::ptr _formatter; //格式化模块对象
        std::vector<LogSink::ptr> _logSinks; //落地方式模块对象，这里是数组，因为考虑到一个日志器模块可能向多个位置进行日志输出
        LogLevel::Level _level; // 日志输出等级，>=_level 才能输出
        std::string _loggerName; //日志器名称
        std::mutex _mtx; //保证多线程下日志输出是安全的
    };
    
    //同步方式落地
    class SyncLogger : public Logger
    {
    public:
        SyncLogger(Formatter::ptr formatter, 
                    std::vector<LogSink::ptr> logSinks,
                    LogLevel::Level level,
                    std::string loggerName) 
                    :Logger(formatter, logSinks, level, loggerName)
                    {}

    private:
        virtual void log(const char* data, size_t len) override
        {
          
            /************************************************************************** 
            这里忘记加锁啦！！！

            同步落地方式不是指单线程落地
            异步落地方式也不是只能多线程才能用
            ***************************************************************************/
           
            std::unique_lock<std::mutex> lock(_mtx);

            if(_logSinks.empty())
                return;
            for(auto& sink : _logSinks)
            {
                sink->log(data, len);
            }
        }
    };

    //异步方式落地
    /*
    异步日志器：
    为了避免业务线程因为写日志的过程时间过程或其他原因导致业务线程阻塞，导致业务线程在写日志的时候影响效率
    为解决这个问题，采用异步的思想，不让业务线程进行实际的落地操作：
    我们让业务线程就将日志消息写到一个缓冲区即可返回，再由一个专门的异步线程，去针对缓冲区中的数据进行处理（实际的落地操作）
    */

    /*
    实现：
        1. 实现一个线程安全的缓冲区
        2. 创建一个异步工作线程，专门负责缓冲区中日志消息的落地操作
    */
    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(Formatter::ptr formatter, 
                    std::vector<LogSink::ptr> logSinks,
                    LogLevel::Level level,
                    std::string loggerName,
                    AsyncType looperType) 
                    :Logger(formatter, logSinks, level, loggerName)
                    //会多一个this指针参数，用绑定
                    ,_asyncLooper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::Working, this, std::placeholders::_1), looperType))
                    {}

        void Working(Buffer& buffer)
        {
            if(_logSinks.empty())
                return;

            //不需要加锁保护
            //这是针对消费缓冲区的处理，没有生产者和生产者，生产者和消费者的竞争
            //有消费者和消费者的竞争，但是就一个工作线程，即就一个消费者
            size_t readSize = buffer.readAbleSize();
            for(auto& sink : _logSinks)
            {
                sink->log(buffer.readBegin(), readSize);
            }
            buffer.moveReader(readSize);
        }

     private:
        virtual void log(const char* data, size_t len) override
        {
            //push的实现是线程安全的，外面不需要加锁
            _asyncLooper->push(data, len);
        }
    private:
        AsyncLooper::ptr _asyncLooper;
    };


    
    //建造者模式
    //1. 抽象一个日志器建造者类（完成日志器对象所需零部件的构造和日志起的构建）
    //    1.设置日志器类型
    //    2.将不同类型日志器的创建放到同一个日志起建造者类中完成
    //2. 派生处具体的建造者类---局部日志建造者和全局日志建造者

    //为什么要用建造者模式
    //因为我们发现，构建上面的 SyncLogger 和 AsyncLooper，要自己提前手动构建许多模块，再用这些参数构造
    //这样做太麻烦，我们用建造者模式来建造日志器，而不要让用户直接去构造日志器，简化用户的使用复杂度

    //定义枚举类
    //同步落地还是异步落地
    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };
    class LoggerBuilder
    {
    public:
        void buildFormatter(const std::string& pattern)
        {
            _formatter = std::make_shared<Formatter>(pattern);
        }
        void buildFormatter(Formatter::ptr formatter)
        {
            _formatter = formatter;
        }

        template<class SinkType, class ...Args>
        void buildLogSink(Args&& ...args)
        {
            _logSinks.push_back(sink_factory.CreateSink<SinkType>(std::forward<Args>(args)...));
        }

        void buildLevel(LogLevel::Level level)
        {
            _level = level;
        }

        void buildLoggerName(const std::string& loggerName)
        {
            _loggerName = loggerName;
        }

        void buildLoggerType(const LoggerType loggerType)
        {
            _loggerType = loggerType;
        }

        void buildTypeUnSafe()
        {
            _asyncType = AsyncType::ASYNC_UNSAFE;
        }

        virtual Logger::ptr build() = 0;

    protected:

        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _logSinks;
        LogLevel::Level _level = LogLevel::Level::DEBUG;
        std::string _loggerName;
        LoggerType _loggerType = LoggerType::LOGGER_SYNC;
        AsyncType _asyncType = AsyncType::ASYNC_SAFE;

        SinkFactory sink_factory;
    };

    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        virtual Logger::ptr build() override
        {
            if(_loggerName.empty())
            {
                std::cout << "日志器名字不能为空" << std::endl;
                abort();
            }

            if(_formatter == nullptr)
                buildFormatter("[%D{%Y.%m.%d %H:%M:%S}][%t][%c][%f:%l][%p]  <%g>%n");

            if(_logSinks.empty())
                buildLogSink<StdioSink>();

            if(_loggerType == LoggerType::LOGGER_ASYNC)
                return std::make_shared<AsyncLogger>(_formatter, _logSinks, _level, _loggerName, _asyncType);
            else
                return std::make_shared<SyncLogger>(_formatter, _logSinks, _level, _loggerName);
        }
    };

   

    /*
    日志器管理类：
    1. 对所有创建的日志起进行管理
    2. 可以在程序任意位置，获取相同单例对象，获取其中的日志起进行日志输出
    3. 设计默认日志器，让用户在不创建任何日志起情况下也能进行标准输出打印，方便用户使用
    */

    // TODO: 想一想，真的需要加那么多的锁吗？

    class LoggerManager
    {
    private:
        LoggerManager() 
        {
            //构建默认日志器

            //这里只能局部建造者，不能全局建造者
            //否则会造成死循环：
            //全局建造者要创建LoggerManager，而LoggerManager构造函数中要创建默认日志器
            //全局建造者创建默认日志器要创建LoggerManager……
            LocalLoggerBuilder builder;         
            builder.buildLoggerName("RootLogger");
            _rootLogger = builder.build();
            _loggersManager.insert({"RootLogger", _rootLogger});
        }
        LoggerManager(const LoggerManager&) = delete;
        ~LoggerManager() {}; 

    public:
        //获取单例模式的句柄
        static LoggerManager& GetInstance()
        {
           static LoggerManager _eton;
           return _eton;
        }

    public:
        bool AddLogger(Logger::ptr& logger)
        {
            if(JudgeLogger(logger->GetLoggerName()))
                return false;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggersManager.insert({logger->GetLoggerName(), logger});
            return true;
        }

        bool JudgeLogger(const std::string& name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if(_loggersManager.count(name))
                return true;
            return false;
        }

        Logger::ptr GetLogger(const std::string& name)
        {
            if(!JudgeLogger(name))
                return nullptr;
            std::unique_lock<std::mutex> lock(_mutex);
            return _loggersManager[name];
        }

        Logger::ptr RootLogger()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _rootLogger;
        }

    private:
        std::mutex _mutex;  //线程安全，可能有多个线程同时插入
        Logger::ptr _rootLogger;  //默认日志器
        std::unordered_map<std::string, Logger::ptr>  _loggersManager;  //管理的日志器
    };

    
    //全局日志器对比局部日志器只多了一个功能：将日志器添加进LoggerManager中
    class GobalLoggerBuilder : public LoggerBuilder
    {
    public:
        virtual Logger::ptr build() override
        {
            if(_loggerName.empty())
            {
                std::cout << "日志器名字不能为空" << std::endl;
                abort();
            }

            if(_formatter == nullptr)
                buildFormatter("[%D{%Y.%m.%d %H:%M:%S}][%t][%c][%f:%l][%p]  <%g>%n");

            if(_logSinks.empty())
                buildLogSink<StdioSink>();

            Logger::ptr logger;
            if(_loggerType == LoggerType::LOGGER_ASYNC)
                logger =  std::make_shared<AsyncLogger>(_formatter, _logSinks, _level, _loggerName, _asyncType);
            else
                logger =  std::make_shared<SyncLogger>(_formatter, _logSinks, _level, _loggerName);
            LoggerManager::GetInstance().AddLogger(logger);
            return logger;
        }
    };

   
}

#endif